import numpy as np

import call_counter
import extend_powell_singular
import extend_powell_singular_slow
import fletcher_freeman
import singular_hessian_test
import trigonometric
import trigonometric_slow
import wood
import wood_slow
from optimizer_util import show_result, wolfe_line_search


def test_singular_hessian_with_ff():
    for create in (singular_hessian_test.create_f1, singular_hessian_test.create_f2):
        print("Testing function with singular hessian: %s" % create)
        x0, f, g, h = create()
        f, g, h, fc, gc, hc = call_counter.wrap(f, g, h)
        x, it_n, it_ls, msg = fletcher_freeman.optimize(f, g, h, x0, 1000, 1e-8, wolfe_line_search)
        show_result(f, x, it_n, it_ls, msg, fc, gc, hc)
        print()


def test_func(n, c1, c2):
    if n is None:
        _, f, g, h = c1.create()
        x0, f1, g1, h1 = c2.create()
    else:
        _, f, g, h = c1.create(n)
        x0, f1, g1, h1 = c2.create(n)
    n = x0.shape[0]
    print("Testing %d-dim %s <--> %s" % (n, c1.__name__, c2.__name__))
    for i in range(100):
        x = np.random.rand(n) * 10.0 - 5.0
        if not np.allclose(f(x), f1(x), rtol=1e-8, atol=1e-12):
            raise ValueError("Function value mismatch")
        if not np.allclose(g(x), g1(x), rtol=1e-8, atol=1e-12):
            raise ValueError("Gradient mismatch")
        if not np.allclose(h(x), h1(x), rtol=1e-8, atol=1e-12):
            raise ValueError("Hessian mismatch")
    print("ok.")
    print("")


# noinspection PyTypeChecker
def main():
    test_singular_hessian_with_ff()
    test_func(None, wood, wood_slow)
    for n in (20, 40, 60, 80, 100):
        test_func(n, extend_powell_singular, extend_powell_singular_slow)
    for n in (20, 40,):  # not checking more, too slow
        test_func(n, trigonometric, trigonometric_slow)


if __name__ == '__main__':
    main()
